package csel.controller.decoders;

import java.util.HashSet;
import java.util.Set;

import csel.controller.bindings.ActionCommandBindings;
import csel.controller.bindings.KeyBindings;
import csel.controller.commandutil.CommandPool;
import csel.controller.dispatchers.Dispatcher;
import csel.controller.enums.ActionEnum;
import csel.controller.enums.CommandEnum;
import csel.controller.events.Event;
import csel.controller.events.KeyPress;
import csel.model.GameCommand;

/**
 * Purpose: Decodes keypresses, implements state logic for menu system
 * 
 * @author Nikolas Wolfe
 */

public class KeyEventDecoder extends EventDecoder<KeyPress>
{
	private final KeyDecoderState inventoryUse;
	private final KeyDecoderState inventoryEquip;
	private final KeyDecoderState inventoryDropUsable;
	private final KeyDecoderState inventoryDropEquippable;
	private final KeyDecoderState skillsUse;
	private final KeyDecoderState skillsUpgrade;
	private final KeyDecoderState skillsProject;
	private final KeyDecoderState equipmentUnequip;
	private final KeyDecoderState gimpThrow;
	private final CommandPool pool;
	private final KeyBindings keyBindings;
	private final ActionCommandBindings commandEnums;
	private KeyDecoderState currentState;
	
	
	public KeyEventDecoder( CommandPool commandPool, Dispatcher disp )
	{
		super(disp);
		this.inventoryUse = new InventoryUseState();
		this.inventoryEquip = new InventoryEquipState();
		this.inventoryDropUsable = new InventoryDropUsableState();
		this.inventoryDropEquippable = new InventoryDropEquippableState();
		this.skillsUse = new SkillsUseState();
		this.skillsUpgrade = new SkillsUpgradeState();
		this.skillsProject = new SkillsProjectState();
		this.equipmentUnequip = new EquipmentUnequipState();
		this.gimpThrow = new GimpThrowState();
		
		this.pool = commandPool;
		this.keyBindings = KeyBindings.getInstance();
		this.commandEnums = ActionCommandBindings.getInstance();
		
		// default state
		this.currentState = skillsUse;
	}

	/**
	 * ========================================================================
	 * All external calls from the superclass to retrieve a GameCommand are 
	 * invoked upon to this method
	 */
	@Override
	protected GameCommand parseEvent(Event event)
	{
		return currentState.parseEvent(event);
	}
	
	/**
	 * ========================================================================
	 * This is the abstract definition of a key decoder state, this implements
	 * the same interface as the context
	 */
	private abstract class KeyDecoderState 
	{
		private Set<ActionEnum> stateEvents;
		private Set<ActionEnum> slotEvents;
		
		public KeyDecoderState()
		{
			this.stateEvents = new HashSet<ActionEnum>();
			stateEvents.add( ActionEnum.ROTATE_SLOTS );
			stateEvents.add( ActionEnum.INVENTORY );
			stateEvents.add( ActionEnum.SKILLS );
			stateEvents.add( ActionEnum.EQUIPMENT );
			stateEvents.add( ActionEnum.CHANGE_SUBVIEW );
			stateEvents.add( ActionEnum.RETURN );
			this.slotEvents = new HashSet<ActionEnum>();
			slotEvents.add( ActionEnum.SLOT_0 );
			slotEvents.add( ActionEnum.SLOT_1 );
			slotEvents.add( ActionEnum.SLOT_2 );
			slotEvents.add( ActionEnum.SLOT_3 );
			slotEvents.add( ActionEnum.SLOT_4 );
			slotEvents.add( ActionEnum.SLOT_5 );
			slotEvents.add( ActionEnum.SLOT_6 );
			slotEvents.add( ActionEnum.SLOT_7 );
			slotEvents.add( ActionEnum.SLOT_8 );
			slotEvents.add( ActionEnum.SLOT_9 );
			
			/**
			 * ====================
			 * *** TRANSITIONS ****
			 * ====================
			 * 
			 * Skills <--> Equip
			 * Skills <--> Inventory
			 * 
			 * Equip  <--> Inventory
			 * Equip  <--> Skills
			 * 
			 * Inventory <--> Skills
			 * Inventory <--> Equip
			 * Inventory  --> Inventory Equip (change subview)
			 * 
			 * Inventory Equip --> Inventory Drop Usable (change subview)
			 * Inventory Drop Equippable --> Throw Item (change subview)
			 * Throw Item --> Inventory (change subview)
			 */
		}
		
		public void activate()
		{
			currentState = this;
		}
		
		protected GameCommand parseEvent( Event event )
		{
			if( !isStateEvent( event ) && !isSlotEvent( event ) )
			{
				ActionEnum actionEnum = event.getActionEnum();
				CommandEnum commandEnum = commandEnums.getEnum( actionEnum );
				if( commandEnum == null)
				{
					throw new IllegalStateException(
							"The program attempted to use an invalid key command");
				}
				return pool.getCommand(commandEnum, event);
			}
			else  
			{
				ActionEnum e = event.getActionEnum();
				if( e == ActionEnum.RETURN ){
					skillsUse.activate();
					return pool.getCommand(commandEnums.getEnum(e), event);
				}
				else if( e == ActionEnum.INVENTORY ){
					inventoryUse.activate();
					return pool.getCommand(commandEnums.getEnum(e), event);
				}
				else if( e == ActionEnum.EQUIPMENT ){
					equipmentUnequip.activate();
					return pool.getCommand(commandEnums.getEnum(e), event);
				}
				else if( e == ActionEnum.SKILLS ){
					skillsUse.activate();
					return pool.getCommand(commandEnums.getEnum(e), event);
				} 
				else if (e == ActionEnum.ROTATE_SLOTS){
					return pool.getCommand(commandEnums.getEnum(e), event);
				} 
				else if (isSlotEvent(event)){
					return pool.getCommand(getCommandEnum(), event);
				}
				return parseStateEvent(event);
			}
		}
		
		private boolean isSlotEvent(Event event)
		{
			return slotEvents.contains( event.getActionEnum() );
		}

		private boolean isStateEvent( Event event )
		{
			return stateEvents.contains( event.getActionEnum() );
		}
		
		protected abstract GameCommand parseStateEvent( Event event );
		
		protected abstract CommandEnum getCommandEnum();
	}
	
	/**
	 * ========================================================================
	 * Iventory Use State
	 */
	private class InventoryUseState extends KeyDecoderState
	{
		@Override
		protected GameCommand parseStateEvent( Event event )
		{
			ActionEnum e = event.getActionEnum();
			if( e == ActionEnum.CHANGE_SUBVIEW ){
				inventoryEquip.activate();
				return pool.getCommand(commandEnums.getEnum(e), event);
			}
			return getHackCommand( this );
		}

		@Override
		protected CommandEnum getCommandEnum()
		{
			return CommandEnum.NB_INVENTORY_USE;
		}
		
		
	}
	
	/**
	 * ========================================================================
	 * Iventory Equip State
	 */
	private class InventoryEquipState extends KeyDecoderState
	{
		@Override
		protected GameCommand parseStateEvent( Event event )
		{
			ActionEnum e = event.getActionEnum();
			if( e == ActionEnum.CHANGE_SUBVIEW ){
				inventoryDropUsable.activate();
				return pool.getCommand(commandEnums.getEnum(e), event);
			}
			return getHackCommand( this );
		}

		@Override
		protected CommandEnum getCommandEnum()
		{
			return CommandEnum.NB_INVENTORY_EQUIP;
		}
	}
	
	/**
	 * ========================================================================
	 * Iventory Drop Usable State
	 */
	private class InventoryDropUsableState extends KeyDecoderState
	{
		@Override
		protected GameCommand parseStateEvent( Event event )
		{
			ActionEnum e = event.getActionEnum();
			if( e == ActionEnum.CHANGE_SUBVIEW ){
				inventoryDropEquippable.activate();
				return pool.getCommand(commandEnums.getEnum(e), event);
			}
			return getHackCommand( this );
		}

		@Override
		protected CommandEnum getCommandEnum()
		{
			return CommandEnum.NB_INVENTORY_DROP_USEABLE;
		}
	}
	
	/**
	 * ========================================================================
	 * Iventory Drop Equippable State
	 */
	private class InventoryDropEquippableState extends KeyDecoderState
	{
		@Override
		protected GameCommand parseStateEvent( Event event )
		{
			ActionEnum e = event.getActionEnum();
			if( e == ActionEnum.CHANGE_SUBVIEW ){
				gimpThrow.activate();
				return pool.getCommand(commandEnums.getEnum(e), event);
			}
			return getHackCommand( this );
		}

		@Override
		protected CommandEnum getCommandEnum()
		{
			return CommandEnum.NB_INVENTORY_DROP_EQUIPPABLE;
		}
	}
	
	/**
	 * ========================================================================
	 * Skills Use State
	 */
	private class SkillsUseState extends KeyDecoderState
	{
		@Override
		protected GameCommand parseStateEvent( Event event )
		{
			ActionEnum e = event.getActionEnum();
			if( e == ActionEnum.CHANGE_SUBVIEW ){
				skillsProject.activate();
				return pool.getCommand(commandEnums.getEnum(e), event);
			}
			return getHackCommand( this );
		}

		@Override
		protected CommandEnum getCommandEnum()
		{
			return CommandEnum.NB_SKILLS_USE;
		}
	}
	
	/**
	 * ========================================================================
	 * Skills Upgrade State
	 */
	private class SkillsUpgradeState extends KeyDecoderState
	{
		@Override
		protected GameCommand parseStateEvent( Event event )
		{
			ActionEnum e = event.getActionEnum();
			if( e == ActionEnum.CHANGE_SUBVIEW ){
				skillsUse.activate();
				return pool.getCommand(commandEnums.getEnum(e), event);
			}
			return getHackCommand( this );
		}

		@Override
		protected CommandEnum getCommandEnum()
		{
			return CommandEnum.NB_SKILLS_UPGRADE;
		}
	}
	
	/**
	 * ========================================================================
	 * Skills Project State
	 */
	private class SkillsProjectState extends KeyDecoderState
	{
		@Override
		protected GameCommand parseStateEvent( Event event )
		{
			ActionEnum e = event.getActionEnum();
			if( e == ActionEnum.CHANGE_SUBVIEW ){
				skillsUpgrade.activate();
				return pool.getCommand(commandEnums.getEnum(e), event);
			}
			return getHackCommand( this );
		}

		@Override
		protected CommandEnum getCommandEnum()
		{
			return CommandEnum.NB_SKILLS_PROJECT;
		}
	}
	
	/**
	 * ========================================================================
	 * Gimp Throw State
	 */
	private class GimpThrowState extends KeyDecoderState
	{

		@Override
		protected GameCommand parseStateEvent( Event event )
		{
			ActionEnum e = event.getActionEnum();
			if( e == ActionEnum.CHANGE_SUBVIEW ){
				inventoryUse.activate();
				return pool.getCommand(commandEnums.getEnum(e), event);
			}
			return getHackCommand( this );
		}

		@Override
		protected CommandEnum getCommandEnum()
		{
			return CommandEnum.NB_INVENTORY_THROW;
		}
		
	}
	
	/**
	 * ========================================================================
	 * Equipment Unequip
	 */
	private class EquipmentUnequipState extends KeyDecoderState
	{
		@Override
		protected GameCommand parseStateEvent( Event event )
		{
			ActionEnum e = event.getActionEnum();
			return pool.getCommand(commandEnums.getEnum(e), event);
		}

		@Override
		protected CommandEnum getCommandEnum()
		{
			return CommandEnum.NB_EQUIPMENT_UNEQUIP;
		}
	}
	
	/**
	 * ========================================================================
	 * Testing HACK HACK HACK HACK!
	 */
	private GameCommand getHackCommand( final KeyDecoderState me )
	{
		return new GameCommand()
		{
			public void execute()
			{
				System.out.println( "STATE: " + me.toString());
			}
		};
	}
}
